#!/usr/bin/python3

"""
pagamiago - free software script to send PayPal payments

Copyright (C) 2018 Alyssa Rosenzweig

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <https://www.gnu.org/licenses/>.
"""

import math
import json
import re
import sys
import os
import codecs
import argparse
import time
import getpass
import random
from inspect import signature

from pagamigo.RequestSession import RequestSession
import pagamigo.validator as Validator
import pagamigo.Authentication as Authentication
import pagamigo.PeerToPeer as PeerToPeer
import pagamigo.Redirect as Redirect
import pagamigo.Donate as Donate

FLOWS = [PeerToPeer, Redirect, Donate]

parser = argparse.ArgumentParser()

parser.add_argument("action", choices=[f.ID for f in FLOWS], nargs="?")
parser.add_argument("--mock", action="store_true")
parser.add_argument("--debug", action="store_true")
parser.add_argument("--base", default="https://www.paypal.com")
parser.add_argument("--country", default="US")
parser.add_argument("--lang", default="en")

ADDED = ["country", "lang", "base"]

for f in FLOWS + [Authentication]:
    for a in f.ARGS:
        if a not in ADDED:
            parser.add_argument("--" + a)

    ADDED = ADDED + list(f.ARGS.keys())

args = parser.parse_args()

if args.debug:
    # Enable requests logging
    import http.client as http_client
    http_client.HTTPConnection.debuglevel = 1


def die(msg, guru=None):
    print("\n" + msg)

    if guru is not None:
        with open("/tmp/pagamigo-dump.html", "wb") as f:
            f.write(guru.encode("utf8"))

        print("Additional information has been dumped to /tmp/pagamigo-dump.html for offline guru meditation.")

    sys.exit(1)

# Continue annoyingly asking a question until the user gets it right


def loop_input_until_valid(initial, question, error, validator):
    validator = validator if validator is not None else lambda x: True

    def master_validator(x): return (x is not None) and (validator(x))

    func = input

    # Redact passwords
    if question.lower() == "password" and not args.debug:
        func = getpass.getpass

    # Maybe we started out okay

    if master_validator(initial):
        return initial

    while True:
        guess = func(question + ": ")

        if master_validator(guess):
            return guess

        print("Invalid " + error + ". Try again.")


def find_flow(name):
    for flow in FLOWS:
        if flow.ID == name:
            return flow


def validate_flow(flow):
    for arg in flow.ARGS:
        (long, short, validator) = flow.ARGS[arg]

        if (validator is None) and short.upper() == "URL":
            validator = validate_redirect_url

        setattr(args, arg, loop_input_until_valid(
            getattr(args, arg), long, short, validator))


def validate_redirect_url(redirect_url):
    return Redirect.extract_base_url(redirect_url) == args.base


def main():
    action = None
    arg_tuple = None

    # As a general rule, pagamigo should be accessible both interactively and
    # by argparse, to support a range of use cases. Command-line interface
    # should be stable. Interactive interface need not be.

    if args.action is None:
        for i in range(len(FLOWS)):
            print("[" + str(i + 1) + "] " + FLOWS[i].DESCRIPTION)

        print("")

        choice = input("What would you like to do (enter its number)? ")

        try:
            choice = int(choice)
        except ValueError:
            die("Please choose the corresponding number.")

        if choice <= 0:
            die("Think you're funny, eh?")

        try:
            args.action = FLOWS[int(choice) - 1].ID
        except IndexError:
            die("Please choose the appropriate number.")

        print("")

    sequence = [Authentication, find_flow(args.action)]

    for flow in sequence:
        validate_flow(flow)

    session = RequestSession(args.base, args.debug, args.mock)

    for flow in sequence:
        sig = signature(flow.flow)
        lst = {a: getattr(args, a) for a in flow.ARGS}

        lst["session"] = session

        if "base" in sig.parameters:
            lst["base"] = args.base

        if "debug" in sig.parameters:
            lst["debug"] = args.debug

        if "mock" in sig.parameters:
            lst["mock"] = args.mock

        flow.flow(**lst)


if __name__ == "__main__":
    main()
